'''
Name    : dbcc.py
Brief   : CAN message encoder/decoder
Author  : kongjiawei
Home    : https://gitee.com/thin-wind/jump
Version : V1.0
History : none
'''

FILE_INPUT = "Demo.dbc"
FILE_OUTPUT = "example"

import re
from decimal import Decimal

# SG_ SignalName(SigTypeDefinition) : StartBit|SignalSize@ByteOrder ValueType (Factor,Offset) [Min|Max] Unit Receiver
_singalRegex = re.compile(r"""SG_ (?P<SignalNameAddType>.*): (?P<StartBit>[0-9]{1,2})\|(?P<SignalSize>[0-9]{1,2})@(?P<ByteOrder>[0-1])(?P<ValueType>[+-]) \((?P<Factor>.*),(?P<Offset>.*)\) \[(?P<Min>.*)\|(?P<Max>.*)\] "(?P<Unit>.*)"(\s{1,2})(?P<Receiver>.*)""")



cheader = "/**\n"
cheader += " * Name: " + FILE_OUTPUT + ".c\n"
cheader += " * Brief: CAN message encoder/decoder\n"
cheader += " * Note: Automatically generated - do not edit.\n"
cheader += " * About: This file was generated by dbcc.py: See <https://gitee.com/thin-wind/pydbcc>\n"
cheader += " * Author: kongjiawei\n"
cheader += " */\n\n"

hheader = "/**\n"
hheader += " * Name: " + FILE_OUTPUT + ".h\n"
hheader += " * Brief: CAN message encoder/decoder\n"
hheader += " * Note: Automatically generated - do not edit.\n"
hheader += " * About: This file was generated by dbcc.py: See <https://gitee.com/thin-wind/pydbcc>\n"
hheader += " * Author: kongjiawei\n"
hheader += " */\n\n"


static_str = '#ifndef NULL\n#define NULL (void*)0\n#endif\n\n\
#define MIN(a, b) ((a) < (b) ? (a) : (b))\n\
#ifndef DBC_MEM_CPY\n\
static void DbcMemCpy(void* dest, const void* src, uint32_t size)\n\
{\n\
    uint32_t i = 0;\n\
    uint8_t* pdest = dest;\n\
    const uint8_t* psrc = src;\n\
    if(NULL == pdest || NULL == psrc)\n\
        return;\n\
    for(i = 0; i < size; i++)\n\
    {\n\
        pdest[i] = psrc[i];\n\
    }\n\
}\n\
#define DBC_MEM_CPY(dest, src, size) DbcMemCpy(dest, src, size)\n\
#endif\n\n'

class CanDataBase:
    _DbcPath = ""                                               # 文件路径
    _NodeNames = []                                             # 网络节点列表
    _BO_Comment = []                                            # 报文帧列表
    _SG_Comment = []                                            # 注解列表
    _MsgAll = []                                                # Can 消息列表
    _ValComment = []                                            # 数值定义表

    def __init__(self, dbcPath):
        self._DbcPath = dbcPath

    # 将网络节点名添加到 _Nodes 列表中
    def _parseNodes(self, line):
        items = line.split(' ')                                 # 以空格分割
        for each in items:
            if each == "BU_:":
                pass
            else:
                self._NodeNames.append(each)                    # 将 each 添加到 _Nodes 列表中
    
    # 解析报文帧
    def _parseMessage(self, line):
        items = line.split(' ')                                 # 以空格分割
        MessageId = int(items[1])                               # 获取报文 ID
        MessageName = items[2].rstrip(':')                      # 获报文名字（去掉尾部的 ':'）
        MessageSize = int(items[3])                             # 获取报文长度
        Transmitter = items[4]                                  # 获取该报文的网络节点名    
        return [MessageId, MessageName, MessageSize, Transmitter]     

        
    # 解析信号
    def _parseSignal(self, line):
        # SG_ SignalName(SigTypeDefinition) : StartBit|SignalSize@ByteOrder ValueType (Factor,Offset) [Min|Max] Unit Receiver
        signal = _singalRegex.match(line)
        SignalNameAddType = signal.group("SignalNameAddType")   # 信号名称和信号类型
        SignalName = SignalNameAddType.split(" ")[0]            # 信号名称
        SignalType = ''                                         # 信号类型初始为空
        if len(SignalNameAddType.split(" ")) > 1:
            SignalType = SignalNameAddType.split(" ")[1]        # 信号类型
        StartBit = int(signal.group("StartBit"))                # 信号起始位
        SignalSize = int(signal.group("SignalSize"))            # 信号长度
        ByteOrder = int(signal.group("ByteOrder"))              # 字节顺序：0 代表 Motorola 格式，1 代表 Inter 格式
        ValueType = signal.group("ValueType")                   # 信号的数值类型：+表示无符号数，-表示有符号数
        Factor = Decimal(signal.group("Factor"))                # 因子
        Offset = Decimal(signal.group("Offset"))                # 偏移量
        Min = Decimal(signal.group("Min"))                      # 信号的最小值
        Max = Decimal(signal.group("Max"))                      # 信号的最大值
        Unit = signal.group("Unit")                             # 信号的物理单位
        Receiver = signal.group("Receiver").split(',')          # 信号的接收节点，可能有多个，以 ',' 间隔
        return [SignalName, SignalType, StartBit, SignalSize, ByteOrder, ValueType, Factor, Offset, Min, Max, Unit, Receiver]

    # 解析报文/信号注解
    def _parseComment(self, line):
        items = line.split(' ')                                 # 以空格分割
        if items[1] == "BO_":                                   # 解析 "BO_" 的注解 
            tmp = [[],[]]
            tmp[0] = int(items[2])                              # ID
            comment = line.split('"')
            tmp[1] = comment[1]                                 # 注解文本内容
            self._BO_Comment.append(tmp)
        elif items[1] == "SG_":                                 # 解析 "SG_" 的注解 
            tmp = [[],[],[]]
            tmp[0] = int(items[2])                              # ID
            tmp[1] = items[3]
            comment = line.split('"')
            tmp[2] = comment[1]                                 # 注解问文本内容
            self._SG_Comment.append(tmp)
        return self

    # 数值表解析
    def _parseValComment(self, line):
        items = line.split(' ')                                 # 以空格分割
        MessageId = int(items[1])                               # 信号所属的报文 ID
        SignalName = items[2]                                   # 信号名
        # 数值表内容，即该信号的有效值分别用什么符号表示。
        ValComment = line[len(items[0]) + len(items[1]) + len(items[2]) + 3:]
        tmp = [MessageId, SignalName, ValComment]
        self._ValComment.append(tmp)                            # 将 tmp 添加到总的 _ValComment 中


    # 加载 DBC 文件
    def Load(self):
        try:
            file = open(self._DbcPath)
        except OSError:
            print(self._DbcPath + " could not be found.")
            return
        
        if self._DbcPath[-4:] != ".dbc":
            print("The file format is invalid.")

        msgTmp = []
        parse_BO_ = False                                       # 是否解析过 "BO_ " 标志

        for line in file:                                       # 按行遍历
            line = line.strip()                                 # 去除前后空格
            if line.startswith("BU_:"):                         # 如果以 "BU_:" 开头
                self._parseNodes(line)                          # 解析网络节点
            elif line.startswith("BO_ "):                       # 如果以 "BO_ " 开头
                msgTmp = self._parseMessage(line)               # 解析报文帧
                parse_BO_ = True
            elif line.startswith("SG_ "):                       # 如果以 "SG_ " 开头
                signal = self._parseSignal(line)                # 解析信号
                msgTmp.append(signal)                           # 将 signal 添加到 msgTmp 中，一条Can消息包含多个 signal
            elif line == "" and parse_BO_:                      # 在遇到空行且已经解析过 "BO_ " 时，说明一条Can消息(包含多个signal)已经解析完毕
                self._MsgAll.append(msgTmp)                     # msgTmp 加到总的消息列表中
                parse_BO_ = False
            elif line.startswith("CM_ "):                       # 如果以 "CM_ " 开头
                self._parseComment(line)                        # 解析报文/信号注解
            elif line.startswith("VAL_ "):                      # 如果以 "VAL_ " 开头
                self._parseValComment(line)                     # 解析数值表
            elif line.startswith("BA_DEF_ "):                   # 如果以 "BA_DEF_ " 开头
                pass
            elif line.startswith("VAL_TABLE_ "):                # 如果以 "VAL_TABLE_ " 开头
                pass
            elif line.startswith("EV_ "):                       # 如果以 "EV_ " 开头
                pass
            else:
                pass

    def generate(self):
        cfile = open(FILE_OUTPUT+'.c', 'w')
        hfile = open(FILE_OUTPUT+'.h', 'w')

        hfile.write(hheader)
        cfile.write(cheader)
        
        hfile.write('#ifndef __' + FILE_OUTPUT.upper() + '_H\n')
        hfile.write('#define __' + FILE_OUTPUT.upper() + '_H\n\n\n\n')
        include_str = '#include "stdint.h"\n\n'
        hfile.write(include_str)
        hfile.write("// #define DBC_MEM_CPY(dest, src, size) memcpy(dest, src, size)\n\n")
        
        cfile.write('#include "' + FILE_OUTPUT + '.h' + '"\n\n\n')
        cfile.write(static_str)

        for msg in self._MsgAll:
            MessageId = msg[0]
            hfile.write("// #define ENABLE_ID_" + hex(MessageId).upper() + '\n')

        hfile.write("\n\n")

        for msg in self._MsgAll:
            MessageId = msg[0]
            MessageName = msg[1]
            MessageSize = msg[2]
            Transmitter = msg[3]
    
            # 文件注释信息
            hfile.write("/**\n")
            hfile.write(" * CANID: " + hex(MessageId) + "\n")
            hfile.write(" * Name: " + MessageName + "\n")
            hfile.write(" * Transmitter: " + Transmitter + "\n")
            hfile.write(" */\n")
            
            # 共用体类型定义
            hfile.write("typedef union " + MessageName + "_Type" + " {\n")
            hfile.write("\tuint8_t msg[" + str(MessageSize) + "];\n")

            OneGroupSignals = []                            # 信号组列表
            SignalType = msg[4][1]                          # 获取第一个信号类型，可用于判断是否复用

            if SignalType == "":                            
                for i in range(4, len(msg)):
                    OneGroupSignals += [msg[i]]
                SortedSignals = sorted(OneGroupSignals, key=lambda x: x[2]) # 按 StartBit 从小到大进行排序

                hfile.write("\tstruct " + MessageName + "_Normal" + " {\n")
                idx = 0
                ReservedIdx = 0
                SameSignalNameIdx = 0
                SigNameGroup = []
                for signal in SortedSignals:
                    SignalName = signal[0]
                    StartBit = signal[2]
                    if idx < StartBit:
                        hfile.write("\t\tuint64_t AutoReserved" + str(ReservedIdx) + ":" + str(StartBit - idx) + ";\n")
                        ReservedIdx += 1
                        idx = StartBit
                    
                    SignalSize = signal[3]
                    idx += SignalSize
                    if SignalName not in SigNameGroup:
                        SigNameGroup.append(SignalName)
                        hfile.write("\t\tuint64_t " + SignalName + ":" + str(SignalSize) + ";\n")
                    else: # 信号名如何相同则给相同的信号名添加后缀，C语言结构体不支持相同元素名
                        SameSignalNameIdx += 1
                        hfile.write("\t\tuint64_t " + SignalName + str(SameSignalNameIdx) + ":" + str(SignalSize) + ";\n")
                
                hfile.write("\t} " + "normal;\n")
            
            elif SignalType == "M":                         # 多路复用
                suffix = ""                                 # 后缀
                for i in range(4, len(msg)):
                    SignalType = msg[i][1]
                    StartBit = msg[i][2]
                    if SignalType == "M":
                        suffix = "m1"
                    elif SignalType != "":
                        suffix = msg[i][1]
                    elif SignalType == "" and StartBit == 0:
                        hfile.write("\tstruct " + MessageName + "_" + suffix + " {\n")
                        SortedSignals = sorted(OneGroupSignals, key=lambda x: x[2]) # 按 StartBit 从小到大进行排序
                        idx = 0
                        ReservedIdx = 0
                        SameSignalNameIdx = 0
                        SigNameGroup = []
                        for signal in SortedSignals:
                            SignalName = signal[0]
                            StartBit = signal[2]
                            if idx < StartBit:
                                hfile.write("\t\tuint64_t AutoReserved" + str(ReservedIdx) + ":" + str(StartBit - idx) + ";\n")
                                ReservedIdx += 1
                                idx = StartBit
                            SignalSize = signal[3]
                            idx += SignalSize
                            if SignalName not in SigNameGroup:
                                SigNameGroup.append(SignalName)
                                hfile.write("\t\tuint64_t " + SignalName + ":" + str(SignalSize) + ";\n")
                            else: # 信号名如何相同则给相同的信号名添加后缀，C语言结构体不支持相同元素名
                                SameSignalNameIdx += 1
                                hfile.write("\t\tuint64_t " + SignalName + str(SameSignalNameIdx) + ":" + str(SignalSize) + ";\n")
                        
                        hfile.write("\t} " + suffix + ";\n")
                        
                        OneGroupSignals = []                    # 清空信号组列表 
                    
                    OneGroupSignals += [msg[i]]                 # 将信号添加到信号组列表中
                    if i == len(msg)-1:                         # 最后一组信号需要单独处理
                        suffix = msg[i][1]
                        hfile.write("\tstruct " + MessageName + "_" + suffix + " {\n")
                        SortedSignals = sorted(OneGroupSignals, key=lambda x: x[2]) # 按 StartBit 从小到大进行排序
                        idx = 0
                        ReservedIdx = 0
                        for signal in SortedSignals:
                            SignalName = signal[0]
                            StartBit = signal[2]
                            if idx < StartBit:
                                hfile.write("\t\tuint64_t AutoReserved" + str(ReservedIdx) + ":" + str(StartBit - idx) + ";\n")
                                ReservedIdx += 1
                                idx = StartBit
                            SignalSize = signal[3]
                            idx += SignalSize
                            hfile.write("\t\tuint64_t " + SignalName + ":" + str(SignalSize) + ";\n")
                        hfile.write("\t} " + suffix + ";\n")
                        

            hfile.write("} " + MessageName + "_Type" + ";\n\n")

            # 共用体变量定义
            cfile.write("#ifdef ENABLE_ID_" + hex(MessageId).upper() + "\n")
            cfile.write(MessageName + "_Type " + MessageName + ";\n")
            cfile.write("#endif\n")

        # 生成can消息解析函数
        cfile.write("\nvoid decode_can_msg(uint32_t id, const uint8_t* data, uint8_t len)\n{\n")
        cfile.write("\tswitch (id)\n\t{\n")

        for msg in self._MsgAll:
            MessageId = msg[0]
            MessageName = msg[1]
            cfile.write("\t\t#ifdef ENABLE_ID_" + hex(MessageId).upper() + "\n")
            cfile.write("\t\tcase ")
            cfile.write(hex(MessageId) + ': ')
            cfile.write("DBC_MEM_CPY(" + MessageName + '.msg, ' + 'data, MIN(sizeof(' + MessageName + '), len)); break;\n')
            cfile.write("\t\t#endif\n")

        cfile.write("\t\tdefault: break;\n")
        cfile.write("\t}\n")
        cfile.write("}\n")

        # 生成对外 API
        hfile.write('\n/********************** API *************************/\n')
        for msg in self._MsgAll:
            MessageId = msg[0]
            MessageName = msg[1]
            hfile.write("#ifdef ENABLE_ID_" + hex(MessageId).upper() + "\n")
            hfile.write("extern " + MessageName + "_Type " + MessageName + ";\n")
            hfile.write("#endif\n")

        hfile.write("\nvoid decode_can_msg(uint32_t id, const uint8_t* data, uint8_t len);\n")


        hfile.write('\n\n#endif\n\n')

        hfile.close()
        cfile.close()

            

def main():
    dbc = CanDataBase(FILE_INPUT)                               # 创建 dbc 对象
    dbc.Load()                                                  # 加载 dbc 文件
    dbc.generate()                                              # 生成 C 代码
    print("Done.")
    input()


if __name__ == "__main__":
    main()